Skip to content

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Feb 6, 2026

Summary

  • add audit sinks and auth providers for command execution
  • audit-first pipeline hooks into JSONL command channel
  • add tests + docs, bump to 0.10.2

Testing

  • pnpm test

Summary by CodeRabbit

  • New Features

    • Audit-first command pipeline with attempt/result event hooks
    • Authentication provider hooks (allow-all and opaque-token)
    • Audit sinks for console and in-memory logging with sensitive-data redaction
    • Authorization error handling for denied access
  • Documentation

    • Updated changelogs and READMEs to reflect audit and auth features and version bump
  • Tests

    • Added unit tests covering audit and authorization workflows

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Adds version 0.10.2 for @git-stunts/alfred and @git-stunts/alfred-live, introducing an audit-first command pipeline, pluggable audit sinks, and authorization hooks integrated into executeCommandLine (attempt/result audit events and auth checks).

Changes

Cohort / File(s) Summary
Version Bumps & Changelogs
CHANGELOG.md, README.md, alfred/CHANGELOG.md, alfred/jsr.json, alfred/package.json, alfred-live/CHANGELOG.md, alfred-live/jsr.json, alfred-live/package.json
Bumped versions to 0.10.2 and added changelog entries; README latest-release text updated to reference the audit/auth changes.
Audit Module
alfred-live/src/audit.js
New audit utilities: redactSensitive, InMemoryAuditSink, ConsoleAuditSink (redacts then logs), and MultiAuditSink (fan-out).
Auth Module
alfred-live/src/auth.js
New auth helpers: allowAllAuth() and opaqueTokenAuth(tokens) with provider/result validation and AuthorizationError usage.
Command Pipeline Integration
alfred-live/src/command-envelope.js
Major integration: parseCommandLine, audit preview/event builders, recordAuditEvent, authorizeCommand, encodeAuditedResult/encodeFailure; executeCommandLine now emits attempt/result audits and enforces authorization.
Errors
alfred-live/src/errors.js
Added AUTH_DENIED error code and AuthorizationError class (extends AlfredLiveError).
Public API & Types
alfred-live/src/index.d.ts, alfred-live/src/index.js
Extended type definitions and exports: audit sink classes, CommandAuditEvent/CommandAuditSink, CommandAuthContext/CommandAuthorizer, allowAllAuth/opaqueTokenAuth, AuthorizationError, and executeCommandLine signature now accepts audit/auth/includeRaw.
Docs
alfred-live/README.md
Added "Audit + Auth Hooks" section with examples and updated status to v0.10.2.
Tests
alfred-live/test/unit/command-pipeline.test.js
New unit tests covering invalid-JSON audit trail and auth-denied path asserting attempt/result audit events.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Pipeline as Command Pipeline
    participant Auth as Auth Provider
    participant Audit as Audit Sink
    participant Handler as Command Handler

    Client->>Pipeline: executeCommandLine(jsonlLine, { auth, audit })
    Pipeline->>Audit: record({ phase: "attempt", ... })
    Pipeline->>Auth: authorize(context)
    alt auth ok
        Auth-->>Pipeline: { ok: true, data: ... }
        Pipeline->>Handler: validate & execute command
        Handler-->>Pipeline: command result
        Pipeline->>Audit: record({ phase: "result", ok: true, result })
        Pipeline-->>Client: audited result
    else auth denied
        Auth-->>Pipeline: { ok: false, error: AuthorizationError }
        Pipeline->>Audit: record({ phase: "result", ok: false, error: "AUTH_DENIED" })
        Pipeline-->>Client: audited failure
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I tapped the audit trail tonight,

Attempts and results in tidy sight,
Tokens checked, sinks hum bright,
Commands hop through auth’s light,
Version 0.10.2 — a joyous bite! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: introducing audit-first pipeline and auth hooks for alfred-live, which aligns with the substantial feature additions across audit.js, auth.js, and command-envelope.js.
Docstring Coverage ✅ Passed Docstring coverage is 84.21% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/v0.10.2-audit-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@alfred-live/src/command-envelope.js`:
- Around line 118-137: buildAuditEvent currently always copies preview.raw into
the event, risking sensitive or large payloads being stored; update
buildAuditEvent to avoid unconditionally including preview.raw—either only
include raw for the "attempt" phase (e.g., if (phase === 'attempt') event.raw =
preview.raw), or require an explicit opt-in flag on preview/options (e.g.,
preview.includeRaw) and otherwise omit or redact raw (e.g., event.raw =
'[REDACTED]'); apply the same rule consistently for both attempt and result
phases and update callers to pass the opt-in when raw is safe to store.
🧹 Nitpick comments (6)
alfred-live/src/audit.js (1)

65-68: A throwing sink will prevent subsequent sinks from receiving the event.

If any sink in the array throws during record(), the exception propagates and remaining sinks are skipped. Consider wrapping each call in a try/catch to ensure fan-out resilience.

♻️ Suggested fix
   record(event) {
     for (const sink of this.#sinks) {
-      sink?.record?.(event);
+      try {
+        sink?.record?.(event);
+      } catch {
+        // Swallow to avoid one sink breaking fan-out to remaining sinks.
+      }
     }
   }
alfred-live/src/auth.js (2)

56-63: Self-validation of the internally constructed provider is unreachable.

The provider object is constructed on lines 40–54 with a valid authorize function, so validateAuthProvider(provider) on line 56 will always return null. This block is dead code. If the intent is to validate external providers in the future, consider extracting this into a shared wrapAuthProvider(provider) helper at that time.


37-69: Passing a non-iterable, non-string, non-nullish value to tokens will throw at new Set(tokens).

Line 38 handles string and null/undefined, but a non-iterable value (e.g., 42, true) will throw TypeError: ... is not iterable. The JSDoc types this as Iterable<string>, so this is a caller contract issue, but if you want full defensive handling (matching the defensive style of the rest of this file), you could add a guard.

alfred-live/src/command-envelope.js (3)

158-182: authorizeCommand returns semantically different data shapes depending on the path.

When no auth provider is supplied, line 160 returns okResult({ allowed: true }) (i.e. { ok: true, data: { allowed: true } }). When an auth provider succeeds, it returns the provider's own Result<unknown>. Since the caller only inspects .ok and never reads .data, this is functionally harmless — but the phantom { allowed: true } data payload is misleading and could confuse future maintainers who expect it to be meaningful.

Consider returning okResult(null) or okResult(undefined) for the no-auth shortcut, consistent with recordAuditEvent's style at line 141.

♻️ Optional: normalize the no-auth return
 function authorizeCommand(auth, context) {
   if (!auth) {
-    return okResult({ allowed: true });
+    return okResult(null);
   }

194-200: Audit recording failure silently swallows the original command result.

If the result-phase audit recording fails (line 196), encodeAuditedResult returns an error envelope for the audit failure and discards resultEnvelope entirely. This means a command that succeeded will look like a failure to the caller if the audit sink throws.

This appears intentional for an "audit-first" design (consistent with the attempt-phase check at lines 440-442), but it's a strong guarantee that couples operational reliability to the audit sink. Worth a brief doc comment here so future readers understand the trade-off.


184-192: buildAuthContext is a field-by-field copy of preview.

This creates an identical object shape to preview. If the intent is API decoupling (so auth providers don't depend on internal preview shape), this is fine. Otherwise, consider simply returning { ...preview } to reduce surface area for drift.

♻️ Optional simplification
 function buildAuthContext(preview) {
-  return {
-    id: preview.id,
-    cmd: preview.cmd,
-    args: preview.args,
-    auth: preview.auth,
-    raw: preview.raw,
-  };
+  const { id, cmd, args, auth, raw } = preview;
+  return { id, cmd, args, auth, raw };
 }

@flyingrobots
Copy link
Member Author

@coderabbitai review please

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

@flyingrobots: I'll review the changes in this PR right away. The audit-first pipeline and auth hooks implementation looks like a substantial addition to the alfred-live package.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@flyingrobots flyingrobots merged commit 476cfdf into main Feb 7, 2026
2 checks passed
@flyingrobots flyingrobots deleted the feat/v0.10.2-audit-auth branch February 7, 2026 00:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant